use std::collections::HashMap;
use std::fs::OpenOptions;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};

use anyhow::{anyhow, Result};
use regex::Regex;
use toml::Value;

use crate::{APP_DIR, WORK_DIR};

use super::{APP_CONFIG_NAME, CONFIG_FILE_NAME, DATA_FILE_NAME};

#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Server {
    pub name: String,
    #[serde(rename = "deployPath")]
    pub deploy_path: String,
}

#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Profile {
    pub server: Vec<Server>,
    pub env: HashMap<String, Value>,
}

#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Local {
    pub command: Vec<String>,
}

#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Remote {
    pub command: Vec<String>,
    pub backup: bool,
    #[serde(rename = "backupFilePath")]
    pub backup_file_path: String,
}

#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct DeployConfig {
    #[serde(rename = "targetName")]
    pub target_name: String,
    #[serde(rename = "targetPath")]
    pub target_path: String,
    pub execution: HashMap<String, Value>,
    pub profile: HashMap<String, Profile>,
    pub local: Local,
    pub remote: Remote,
    pub after: Local,
}

fn check_config_path() -> Result<PathBuf> {
    let current_dir = WORK_DIR.get().unwrap_or_else(|| panic!("获取工作目录失败！"));
    let config_path = Path::new(current_dir).join(CONFIG_FILE_NAME);
    if !config_path.exists() {
        return Err(anyhow!("配置文件不存在！"));
    }
    Ok(config_path)
}

pub fn read_config() -> Result<DeployConfig> {
    let config_path = check_config_path()?;
    match OpenOptions::new().read(true).open(config_path) {
        Ok(mut fs) => {
            let mut content = String::new();
            fs.read_to_string(&mut content)?;
            let config: DeployConfig = toml::from_str(&content)?;
            Ok(config)
        }
        Err(err) => Err(anyhow!(err.to_string()))
    }
}

pub fn init_config(current_dir: String) -> Result<()> {
    let config_path = Path::new(&current_dir).join(CONFIG_FILE_NAME);
    let config_str = r#"# 部署文件名称
        targetName = "test"
        # 部署文件目录，默认当前文件夹
        targetPath = ""
        # 配置不同环境设置 server name对应服务器管理中的label deploy_path 为远程部署路径
        [[profile.dev.server]]
        name = "test"
        deployPath= ""
        # 环境变量 默认添加profile变量deploy.active 环境变量使用格式 deploy.test
        [profile.dev.env]
        test = 1
        [local]
        # 本地执行命令
        command = [""]
        [remote]
        # 远程执行命令
        command = ["cd {deployPath} && python3 {targetName}"]
        # 是否对远程文件进行备份
        backup = false
        # 备份文件路径
        backupFilePath = ""
        # 设置可执行文件的环境变量
        [execution]
        JAVA_HOME = ""
        # 部署完成后执行的命令，用于清理编译文件等。
        [after]
        command = ["echo 'success'"]"#;
    let config_str = Regex::new(r"(?m)^ +")?.replace_all(config_str, "").to_string();
    if let Ok(mut fs) = OpenOptions::new().write(true).truncate(true).create_new(true).open(config_path) {
        fs.write_all(config_str.as_bytes())?;
    }
    Ok(())
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct AppConfig {
    pub database: Option<PathBuf>,
    pub envs: HashMap<String, Value>,
    pub execution: HashMap<String, Value>,
}

impl AppConfig {
    pub fn init() -> Result<Self> {
        let (data_path, app_cofing) = AppConfig::get_app_config_path()?;
        if app_cofing.exists() {
            match OpenOptions::new().read(true).open(app_cofing) {
                Ok(mut fs) => {
                    let mut content = String::new();
                    fs.read_to_string(&mut content)?;
                    let mut config: AppConfig = toml::from_str(&content)?;
                    if config.database.as_ref().is_none() {
                        config.database = Some(data_path.join(DATA_FILE_NAME));
                    }
                    Ok(config)
                }
                Err(err) => { Err(anyhow!(err.to_string())) }
            }
        } else {
            let config = Self { database: Some(data_path.join(DATA_FILE_NAME)), envs: HashMap::default(), execution: HashMap::default() };
            config.save_config()?;
            Ok(config)
        }
    }

    fn get_app_config_path() -> Result<(PathBuf, PathBuf)> {
        let app_data_path = APP_DIR.get().expect("获取应用数据目录失败！").clone();
        let data_path = Path::new(&app_data_path);
        if !data_path.exists() {
            std::fs::create_dir(data_path)?;
        }
        let app_cofing = data_path.join(APP_CONFIG_NAME);
        Ok((data_path.to_path_buf(), app_cofing))
    }

    fn save_config(&self) -> Result<()> {
        let (_, app_cofing) = AppConfig::get_app_config_path()?;
        let mut fs = OpenOptions::new().write(true).create(true).truncate(true).open(app_cofing)?;
        let toml_str = toml::to_string(self)?;
        fs.write_all(toml_str.as_bytes())?;
        Ok(())
    }
}